package com.code.ape.codeape.device.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.code.ape.codeape.admin.api.entity.SysUser;
import com.code.ape.codeape.admin.api.feign.RemoteUserService;
import com.code.ape.codeape.common.core.constant.SecurityConstants;
import com.code.ape.codeape.common.core.exception.ServiceException;
import com.code.ape.codeape.common.core.util.AssertUtil;
import com.code.ape.codeape.common.core.util.R;
import com.code.ape.codeape.common.core.util.RetOps;
import com.code.ape.codeape.common.core.util.SpringContextHolder;
import com.code.ape.codeape.common.security.service.CodeapeUser;
import com.code.ape.codeape.common.security.util.SecurityUtils;
import com.code.ape.codeape.device.api.dto.QcRecordDTO;
import com.code.ape.codeape.device.api.entity.*;
import com.code.ape.codeape.device.api.vo.QcRecordVO;
import com.code.ape.codeape.device.event.QcRecordEvent;
import com.code.ape.codeape.device.mapper.QcRecordMapper;
import com.code.ape.codeape.device.mapper.QcRecordUniqueMapper;
import com.code.ape.codeape.device.service.*;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 质控记录
 * 该实现类用于实现WEB端的逻辑
 *
 * @author 公众号：码猿技术专栏   版权：不才陈某所署，侵权必究
 * @date 2023-05-28 12:15:42
 */
@Service
@RequiredArgsConstructor
@Primary
@Slf4j
public class QcRecordServiceImpl extends ServiceImpl<QcRecordMapper, QcRecord> implements QcRecordService {

	@Getter
	private final QcLiquidInfoService qcLiquidInfoService;
	@Getter
	private final PaperInfoService paperInfoService;
	@Getter
	private final DeviceInfoService deviceInfoService;
	@Getter
	private final QcRecordUniqueMapper qcRecordUniqueMapper;
	@Getter
	private final RemoteUserService remoteUserService;

	@Getter
	private final QcRecordUniqueService qcRecordUniqueService;


	/**
	 * 添加质控记录
	 * 1. 存在并发问题，这里利用防重表，避免出现重复添加
	 * @param dto 质控记录
	 */
	/**
	 * todo 由于PDA存在离线场景，可能存在以下场景：
	 * 1. 质控液和试纸已经被删除了，但是此时由于PDA是离线的状态，并不知道，所有可能存在护士在做质控的
	 * 	时候选择了已经删除的质控液和试纸，这里需要做兼容，PDA的上传不做这一步校验，后续补充
	 * 2. PDA的质控逻辑是由本地生成，因此不需要重复计算
	 * 当然这个接口单条添加，属于在线接口，仍然需要校验
	 */
	@Override
	@Transactional
	public Boolean saveQcRecord(QcRecordDTO dto) {
		//step1 校验SN号
		DeviceInfo deviceInfo = deviceInfoService.getOne(Wrappers.<DeviceInfo>lambdaQuery().eq(DeviceInfo::getSn, dto.getSn()));
		AssertUtil.isTrue(Objects.nonNull(deviceInfo), "设备不存在！");
		//step2 校验质控液、试纸是否存在
		PaperInfo paperInfo = paperInfoService.getById(dto.getPaperId());
		AssertUtil.isTrue(Objects.nonNull(paperInfo), "试纸不存在！");
		QcLiquidInfo liquidInfo = qcLiquidInfoService.getById(dto.getLiquidId());
		AssertUtil.isTrue(Objects.nonNull(liquidInfo), "质控液不存在！");
		QcRecord qcRecord = new QcRecord();
		BeanUtil.copyProperties(dto, qcRecord);
		//step3 计算结果，此处兼容PDA的逻辑，PDA的结果由本地计算
		if (Objects.isNull(dto.getResult())){
			int result = getQcResult(dto, paperInfo, liquidInfo);
			qcRecord.setResult(result);
		}
		// step4 填充必要的信息
		CodeapeUser codeapeUser = Objects.requireNonNull(SecurityUtils.getUser());
		qcRecord.setHosId(codeapeUser.getHosId());
		qcRecord.setOperatorUserId(codeapeUser.getId());
		

		//step5 先插入防重表
		try{
			QcRecordUnique qcRecordUnique = new QcRecordUnique();
			BeanUtil.copyProperties(qcRecord, qcRecordUnique);
			//此处插入防重表，一旦出现重复数据，则会抛出异常，这里不做任何处理，直接由全局异常处理器捕获，抛出系统错误的异常
			int uniqueRows = qcRecordUniqueMapper.insert(qcRecordUnique);
			AssertUtil.isTrue(uniqueRows == 1, "添加失败！");
			//设置防重表的ID，这样关联起来
			qcRecord.setId(qcRecordUnique.getId());
		}catch (Exception ex){
			throw new ServiceException("重复添加！");
		}

		//step6 插入质控记录
		int qcRecordRows = baseMapper.insert(qcRecord);
		AssertUtil.isTrue(qcRecordRows == 1, "添加失败！");

		//step7 更新设备的最近一次质控时间，这里采用spring event 异步解耦的方式
		SpringContextHolder.publishEvent(new QcRecordEvent(deviceInfo));
		return Boolean.TRUE;
	}

	/**
	 * PDA业务端处理，此处直接返回不支持
	 * @param dtos 质控记录集合
	 */
	@Override
	public Boolean saveBatchQcRecord(List<QcRecordDTO> dtos) {
		AssertUtil.isTrue(false,"该客户端不支持该接口");
		return Boolean.FALSE;
	}

	@Transactional
	@Override
	public Boolean deleteQcRecord(Long id) {
		//step1 删除防重表中数据，直接删除，不是逻辑删除
		int uniqueRows = qcRecordUniqueMapper.deleteById(id);
		AssertUtil.isTrue(uniqueRows == 1, "删除失败！");
		//step2 删除qc_record表中的数据，逻辑删除
		int qcRecordRows = baseMapper.deleteById(id);
		AssertUtil.isTrue(qcRecordRows == 1, "删除失败！");
		return Boolean.TRUE;
	}

	@Transactional
	@Override
	public Boolean updateQcRecord(QcRecordDTO dto) {
		//step1 查看质控记录
		QcRecord qcRecord = baseMapper.selectById(dto.getId());
		AssertUtil.isTrue(Objects.nonNull(qcRecord), "质控记录不存在");
		//step2 如果SN号变了，校验
		if (StrUtil.isNotBlank(dto.getSn()) && !StrUtil.equals(dto.getSn(), qcRecord.getSn())) {
			DeviceInfo deviceInfo = deviceInfoService.getOne(Wrappers.<DeviceInfo>lambdaQuery().eq(DeviceInfo::getSn, dto.getSn()));
			AssertUtil.isTrue(Objects.nonNull(deviceInfo), "该设备不存在！");
		}
		//step3 校验质控液、试纸是否存在
		PaperInfo paperInfo = paperInfoService.getById(dto.getPaperId());
		AssertUtil.isTrue(Objects.nonNull(paperInfo), "试纸不存在！");

		QcLiquidInfo liquidInfo = qcLiquidInfoService.getById(dto.getLiquidId());
		AssertUtil.isTrue(Objects.nonNull(liquidInfo), "质控液不存在！");
		QcRecord qcRecordNew = new QcRecord();
		BeanUtil.copyProperties(dto, qcRecordNew);
		//step3 重新计算，只要其中有一个值变了，就需要重新计算结果
		if ((Objects.nonNull(dto.getValue()) && !dto.getValue().equals(qcRecord.getValue()))
				|| (Objects.nonNull(dto.getLiquidId()) && !dto.getLiquidId().equals(qcRecord.getLiquidId()))
				|| (Objects.nonNull(dto.getPaperId()) && !dto.getPaperId().equals(qcRecord.getPaperId()))) {
			int result = getQcResult(dto,paperInfo,liquidInfo );
			qcRecordNew.setResult(result);
		}
		//step5 先插入防重表
		QcRecordUnique qcRecordUnique = new QcRecordUnique();
		BeanUtil.copyProperties(qcRecordNew, qcRecordUnique);
		//此处更新防重表，一旦出现重复数据，则会抛出异常，这里不做任何处理，直接由全局异常处理器捕获，抛出系统错误的异常
		int uniqueRows = qcRecordUniqueMapper.updateById(qcRecordUnique);
		AssertUtil.isTrue(uniqueRows == 1, "修改失败！");
		//step6 更新质控记录
		int qcRecordRows = baseMapper.updateById(qcRecordNew);
		AssertUtil.isTrue(qcRecordRows == 1, "修改失败！");
		return Boolean.TRUE;

	}

	@Override
	public Page<QcRecordVO> listPage(Page<QcRecordVO> page, QcRecordDTO dto) {
		Page<QcRecordVO> pageVo = baseMapper.selectQcRecordPage(page, dto);
		//step2 获取入库人姓名
		List<QcRecordVO> records = pageVo.getRecords();
		//入库人的全部的id
		Set<Long> enterIds = records.stream().filter(Objects::nonNull).map(QcRecordVO::getOperatorUserId).collect(Collectors.toSet());

		//step3 调用admin服务获取，入库人姓名不是展示必须，即使服务调用失败也不抛出异常，try起来
		if (CollectionUtil.isNotEmpty(enterIds)) {
			List<SysUser> enterList = new ArrayList<>();
			try {
				R<List<SysUser>> resultEnterUser = remoteUserService.listUserInfoByIds(enterIds);
				enterList = RetOps.of(resultEnterUser).getData().orElse(new ArrayList<>());
			} catch (Exception ex) {
				log.error("调用用户服务异常，参数：{}，异常信息：{}", JSON.toJSON(enterIds), ex.getLocalizedMessage());
			}

			if (CollectionUtil.isNotEmpty(enterList)) {
				Map<Long, SysUser> userMap = enterList.stream().collect(Collectors.groupingBy(SysUser::getUserId, Collectors.collectingAndThen(Collectors.toList(), list -> list.get(0))));
				records.stream().peek(entity -> entity.setOperatorName(Optional.ofNullable(userMap.get(entity.getOperatorUserId())).orElse(new SysUser()).getName())).collect(Collectors.toList());
			}
		}
		return pageVo;
	}

	private int getQcResult(QcRecordDTO dto, PaperInfo paperInfo, QcLiquidInfo liquidInfo) {
		//低浓度
		if (liquidInfo.getType() == QcLiquidEnum.LOW_TYPE.getValue()) {
			return dto.getValue() >= paperInfo.getLowMinLimit() && dto.getValue() <= paperInfo.getLowMaxLimit() ? 1 : 0;
		} else if (liquidInfo.getType() == QcLiquidEnum.MIDDLE_TYPE.getValue()) {
			return dto.getValue() >= paperInfo.getMediumMinLimit() && dto.getValue() <= paperInfo.getMediumMaxLimit() ? 1 : 0;
		} else {
			return dto.getValue() >= paperInfo.getHighMinLimit() && dto.getValue() <= paperInfo.getHighMaxLimit() ? 1 : 0;
		}
	}

	/**
	 * 支持WEB端
	 * @param clientId 客户端ID
	 * @return
	 */
	@Override
	public boolean support(String clientId) {
		return StrUtil.equals(SecurityConstants.WEB,clientId);
	}

	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE;
	}
}
